﻿using System;
using CSCore;
using CSCore.Codecs.WAV;
using CSCore.CoreAudioAPI;
using System.Windows.Forms;
using CSCore.SoundIn;
using CSCore.Streams;
using CSCore.Codecs;
using CSCore.MediaFoundation;
using System.Linq;
using System.Threading;
using System.IO;
using Timer = System.Windows.Forms.Timer;
using System.Diagnostics;

namespace WsapiAudioRecorder
{
    public partial class Form_Main : Form
    {
        private WasapiCapture _soundIn;
        private IWriteable _writer;
        private IWaveSource _finalSource;
        private string _output_filename = "out.mp3";
        const string _tempfilename = "temp.wav";
        string[] args;
        float auto_record_time;
        string[] args_flags = new string[] { "-p", "-art", "-t", "-h", "-acp","-help" };
        bool on_reading;
        Timer tim, check_tim;
        bool delay_finished;
        bool ishide_onstart;
        bool is_auto_complete;
        bool is_auto_start;
        bool auto_stop_with_dur;
        bool is_recieved_audio_data;

        public Form_Main(string[] args)
        {
            InitializeComponent();

            this.notifyIcon1.Visible = true;
            this.btn_stoprecord.Enabled = false;
            this.args = args;

            //args = new string[] { "-help" };
            //args = new string[] { "-p", "D:/aa.mp3", "-t", "-acp", "-art", "-1" };

            //删除临时文件
            if (File.Exists(_tempfilename))
            {
                File.Delete(_tempfilename);
            }

            //path toplevel autorecordtime autocomplete
            //-p -t -art -acp -help
            if (args.Length >= 1)
            {
                try
                {
                    for (int i = 0; i < args.Length; i++)
                    {
                        if (args_flags.Contains(args[i]))
                        {
                            switch (args[i])
                            {
                                case "-help": MessageBox.Show("-p path [-t:TopMost] [-h:Hide] [-acp]:auto complete] [-art:auto stop after sometime]","参数说明"); break;//帮助
                                case "-p": this._output_filename = args[i + 1]; break;//音频文件完整保存路径
                                case "-t": this.TopMost = true; break;  //置顶窗口
                                case "-h": ishide_onstart = true; break;//启动时隐藏窗口
                                case "-acp":is_auto_complete = true;break;//自动完成，从开始接收到有效音频开始检测，如果后面持续指定时间没有接收到有效音频数据，则自动完成。
                                case "-art"://自动录音，后面接录音的时长，如果时长t>0,则在时间t后自动关闭。
                                    this.auto_record_time = float.Parse(args[i + 1]);
                                    //自动启动录音
                                    is_auto_start = true;
                                    if (auto_record_time > 0)
                                    {
                                        //指定时间后自动停止
                                        auto_stop_with_dur = true;
                                    }
                                    break;
                                default:
                                    break;
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    MessageBox.Show("参数错误：" + e.Message);
                    return;
                }

                //自动启动
                if (is_auto_start) StartCapture();
                //启动时隐藏
                if (ishide_onstart) this.Hide();

            }
        }

        private void AutoStopCapture()
        {
            Thread t = new Thread(() =>
            {
                Thread.Sleep((int)(auto_record_time * 1000));
                Close();
            });
            t.Start();
        }

        public void StartCapture(string fileName = _tempfilename)
        {
            Console.WriteLine("start capture");

            if (is_auto_complete)
            {
                //开始进行6s的延时，如果过了这个时间没有检测到任何声音则自动关闭
                Thread thread = new Thread(() =>
                {
                    Thread.Sleep(6000);
                    delay_finished = true;
                    if (auto_stop_with_dur)
                    {
                        AutoStopCapture();
                    }

                });
                thread.Start();

                //在开始的延时结束后，如果监测到没有再接收音频数据，则停止不做音频
                check_tim = new Timer();
                check_tim.Interval = 1000;
                check_tim.Tick += (sender, e) =>
                {
                    if (delay_finished && on_reading == false)
                    {
                        StopCapture(true);
                    }
                };
                check_tim.Start();


                //每隔0.5s设置把on_reading设为false,等待下方代码的修正
                tim = new Timer();
                tim.Interval = 500;
                tim.Tick += (sender, e) =>
                {
                    on_reading = false;
                };
                tim.Start();
            }

            _soundIn = new WasapiLoopbackCapture(20);
            _soundIn.Device = MMDeviceEnumerator.DefaultAudioEndpoint(DataFlow.Render, Role.Multimedia);
            _soundIn.Initialize();

            var soundInSource = new SoundInSource(_soundIn);
            var singleBlockNotificationStream = new SingleBlockNotificationStream(soundInSource.ToSampleSource());
            _finalSource = singleBlockNotificationStream.ToWaveSource();
            _writer = new WaveWriter(fileName, _finalSource.WaveFormat);

            byte[] buffer = new byte[_finalSource.WaveFormat.BytesPerSecond / 2];
            soundInSource.DataAvailable += (s, e) =>
            {
                //接收到了音频数据
                is_recieved_audio_data = true;
                //只要在持续接收音频数据，就会把onreading设为true
                on_reading = true;
                int read;
                while ((read = _finalSource.Read(buffer, 0, buffer.Length)) > 0)
                {
                    _writer.Write(buffer, 0, read);
                }
            };

            _soundIn.Start();
            this.btn_startrecord.Enabled = false;
            this.btn_stoprecord.Enabled = true;
        }

        public void StopCapture(bool isdelayed = false)
        {
            Console.WriteLine("stop capture");
            if (_soundIn != null)
            {
                _soundIn.Stop();
                _soundIn.Dispose();
                _soundIn = null;
                _finalSource.Dispose();
                _finalSource = null;

                if (_writer is IDisposable)
                {
                    ((IDisposable)_writer).Dispose();
                    _writer = null;
                }

                if (is_recieved_audio_data)
                {
                    ConvertWav2Mp3(_tempfilename, _output_filename, isdelayed);
                    Process.Start("explorer.exe", Path.GetDirectoryName(_output_filename));
                }

                Close();
            }
        }

        private void ConvertWav2Mp3(string srcfile = _tempfilename, string dstfile = "out.mp3",bool isdelayed = false)
        {

            if (File.Exists(_output_filename))
            {
                File.Delete(_output_filename);
            }

            var supportedFormats = MediaFoundationEncoder.GetEncoderMediaTypes(AudioSubTypes.MpegLayer3);
            if (!supportedFormats.Any())
            {
                Console.WriteLine("The current platform does not support mp3 encoding.");
                return;
            }

            IWaveSource source;
            try
            {
                source = CodecFactory.Instance.GetCodec(srcfile);

                if (
                    supportedFormats.All(
                        x => x.SampleRate != source.WaveFormat.SampleRate && x.Channels == source.WaveFormat.Channels))
                {
                    //the encoder does not support the input sample rate -> convert it to any supported samplerate
                    //choose the best sample rate with stereo (in order to make simple, we always use stereo in this sample)
                    int sampleRate =
                        supportedFormats.OrderBy(x => Math.Abs(source.WaveFormat.SampleRate - x.SampleRate))
                            .First(x => x.Channels == source.WaveFormat.Channels)
                            .SampleRate;

                    Console.WriteLine("Samplerate {0} -> {1}", source.WaveFormat.SampleRate, sampleRate);
                    Console.WriteLine("Channels {0} -> {1}", source.WaveFormat.Channels, 2);
                    source = source.ChangeSampleRate(sampleRate);
                }
            }
            catch (Exception)
            {
                Console.WriteLine("Format not supported.");
                return;
            }

            using (source)
            {
                Console.WriteLine(source.GetLength().TotalSeconds);
                int total_amount = (int)(source.GetLength().TotalSeconds);
                int read_count = 0;
                using (var encoder = MediaFoundationEncoder.CreateMP3Encoder(source.WaveFormat, dstfile))
                {
                    byte[] buffer = new byte[source.WaveFormat.BytesPerSecond];
                    int read;
                    while ((read = source.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        read_count++;
                        if (isdelayed && read_count >= total_amount - 7) break;
                        encoder.Write(buffer, 0, read);
                    }
                }
            }
        }

        private void notifyIcon1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (this.WindowState == FormWindowState.Minimized)
            {
                this.Show();
                this.WindowState = FormWindowState.Normal;
                notifyIcon1.Visible = false;
                this.ShowInTaskbar = true;
            }
        }

        private void Form1_SizeChanged(object sender, EventArgs e)
        {
            if (this.WindowState == FormWindowState.Minimized)
            {
                this.Hide();
                this.ShowInTaskbar = true;
                this.notifyIcon1.Visible = true;
            }
        }

        private void OnStartRecordClick(object sender, EventArgs e)
        {
            StartCapture();
        }

        private void OnStopRecordClick(object sender, EventArgs e)
        {
            StopCapture();
        }
    }
}

